#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include <psp2/kernel/threadmgr.h>

#include <vita2d.h>

#include "text.h"
#include "utils.h"
#include "ui.h"

#define TEXT_PADDING 10.0f

#define TEXT_LINE_SPACE 4.0f
#define TEXT_COLOR WHITE

#define TEXT_BG_COLOR COLOR_ALPHA(BLACK, 0xCF)

static int text_runloop = 0;
static int auto_scroll = 0;
static SceUID text_thid = -1;
static SceKernelLwMutexWork text_mutex;

static TextList text_list;
static int text_draw_lines = 0;
static int top_pos = 0;

static float view_sx, view_sy, view_dx, view_dy, text_sx, text_sy;
static float view_width, view_height, text_width, text_y_space;

void initTextViewDrawInfo()
{
    float line_height = UiGetLineHeight();

    view_width = SCREEN_WIDTH;
    view_height = SCREEN_HEIGHT;

    view_sx = (SCREEN_WIDTH - view_width) / 2;
    view_sy = (SCREEN_HEIGHT - view_height) / 2;
    view_dx = view_sx + view_width;
    view_dy = view_sy + view_height;

    float text_full_height = view_height - TEXT_PADDING * 2;
    text_y_space = line_height + TEXT_LINE_SPACE;
    text_draw_lines = (text_full_height + TEXT_LINE_SPACE) / text_y_space;

    text_sx = view_sx + TEXT_PADDING;
    text_sy = view_sy + TEXT_PADDING;
}

TextListEntry *textListGetEntryByNumber(TextList *list, int n)
{
    if (!list)
        return NULL;

    TextListEntry *entry = list->head;

    while (n > 0 && entry)
    {
        n--;
        entry = entry->next;
    }

    if (n != 0)
        return NULL;

    return entry;
}

void textListAddEntry(TextList *list, TextListEntry *entry)
{
    if (!list || !entry)
        return;

    sceKernelLockLwMutex(&text_mutex, 1, NULL);

    entry->next = NULL;
    entry->previous = NULL;

    if (list->head == NULL)
    {
        list->head = entry;
        list->tail = entry;
    }
    else
    {
        TextListEntry *tail = list->tail;
        tail->next = entry;
        entry->previous = tail;
        list->tail = entry;
    }

    list->length++;

    sceKernelUnlockLwMutex(&text_mutex, 1);
}

void textListAddEntryFromString(TextList *list, const char *string)
{
    if (!list)
        return;

    TextListEntry *entry = (TextListEntry *)malloc(sizeof(TextListEntry));
    entry->text = (char *)malloc(strlen(string) + 1);
    strcpy(entry->text, string);

    textListAddEntry(list, entry);
}

void textListEmpty(TextList *list)
{
    if (!list)
        return;

    sceKernelLockLwMutex(&text_mutex, 1, NULL);

    TextListEntry *entry = list->head;

    while (entry)
    {
        TextListEntry *next = entry->next;
        free(entry->text);
        free(entry);
        entry = next;
    }

    list->head = NULL;
    list->tail = NULL;
    list->length = 0;

    sceKernelUnlockLwMutex(&text_mutex, 1);
}

TextList *getDefaultTextList()
{
    return &text_list;
}

void drawTextView()
{
    sceKernelLockLwMutex(&text_mutex, 1, NULL);

    if (splash_tex)
        vita2d_draw_texture_scale(splash_tex, 0.0f, 0.0f, splash_x_scale, splash_y_scale);

    vita2d_draw_rectangle(view_sx, view_sy, view_width, view_height, TEXT_BG_COLOR);

    TextListEntry *entry = textListGetEntryByNumber(&text_list, top_pos);
    if (entry)
    {
        float sx = text_sx;
        float sy = text_sy;

        int i;
        for (i = top_pos; i < text_list.length && i < (top_pos + text_draw_lines); i++)
        {
            UiDrawText(sx, sy, TEXT_COLOR, entry->text);
            sy += text_y_space;
            entry = entry->next;
        }

        drawScrollBar(view_dx - SCROLL_BAR_WIDTH, view_sy, view_height, text_draw_lines, text_list.length, top_pos);
    }

    sceKernelUnlockLwMutex(&text_mutex, 1);
}

int ctrlTextView()
{
    sceKernelLockLwMutex(&text_mutex, 1, NULL);

    if (auto_scroll)
    {
        controlRefreshListPosNoFocus(LIST_CONTROL_DOWN, &top_pos, text_list.length, text_draw_lines);
    }
    else
    {
        readPad();

        if (hold_pad[PAD_UP] || hold2_pad[PAD_LEFT_ANALOG_UP])
        {
            controlRefreshListPosNoFocus(LIST_CONTROL_UP, &top_pos, text_list.length, text_draw_lines);
        }
        else if (hold_pad[PAD_DOWN] || hold2_pad[PAD_LEFT_ANALOG_DOWN])
        {
            controlRefreshListPosNoFocus(LIST_CONTROL_DOWN, &top_pos, text_list.length, text_draw_lines);
        }

        if (released_pad[PAD_CANCEL] || released_pad[PAD_PSBUTTON])
        {
            text_runloop = 0;
        }
    }

    sceKernelUnlockLwMutex(&text_mutex, 1);

    return 0;
}

int loopTextView()
{
    text_runloop = 1;

    while (text_runloop)
    {
        UiStartDrawing();
        drawTextView();
        UiEndDrawing();
        ctrlTextView();
    }

    return 0;
}

void textViewSetAutoScroll(int scroll)
{
    if (auto_scroll)
    {
        top_pos = text_list.length;
        refreshListPosNoFocus(&top_pos, text_list.length, text_draw_lines);
    }
    auto_scroll = scroll;
}

int initTextView()
{
    sceKernelCreateLwMutex(&text_mutex, "text_view_mutex", 2, 0, NULL);

    sceKernelLockLwMutex(&text_mutex, 1, NULL);

    initTextViewDrawInfo();

    memset(&text_list, 0, sizeof(text_list));
    top_pos = 0;

    sceKernelUnlockLwMutex(&text_mutex, 1);

    return 0;
}

int finishTextView()
{
    textListEmpty(&text_list);
    sceKernelDeleteLwMutex(&text_mutex);

    return 0;
}

static int TextViewThread(SceSize args, void *argp)
{
    initTextView();
    loopTextView();
    finishTextView();

    return sceKernelExitDeleteThread(0);
}

int initTextViewThread()
{
    if (text_thid != -1)
        finishTextViewThread();

    text_thid = sceKernelCreateThread("text_view_thread", TextViewThread, 0x10000100, 0x10000, 0, 0, NULL);
    if (text_thid >= 0)
        sceKernelStartThread(text_thid, 0, NULL);

    return 0;
}

int waitTextViewThreadEnd()
{
    if (text_thid >= 0)
    {
        sceKernelWaitThreadEnd(text_thid, NULL, NULL);
        sceKernelDeleteThread(text_thid);
    }
    text_thid = -1;

    return 0;
}

int finishTextViewThread()
{
    text_runloop = 0;
    if (text_thid >= 0)
        waitTextViewThreadEnd();

    return 0;
}